package com.wyw.hemerocallis.service.async;

import com.alibaba.fastjson.JSON;
import com.wyw.hemerocallis.constant.GoodsConstant;
import com.wyw.hemerocallis.entity.HemerocallisGoods;
import com.wyw.hemerocallis.goods.GoodsInfo;
import com.wyw.hemerocallis.goods.SimpleGoodsInfo;
import com.wyw.hemerocallis.repository.HemerocallisGoodsRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 异步服务接口实现
 *
 * @author Mr Wu    yewen.wu.china@gmail.com
 * <p>
 * Update History:
 * Author        Time            Content
 */
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class AsyncServiceImpl implements IAsyncService {

    private final HemerocallisGoodsRepository hemerocallisGoodsRepository;
    private final StringRedisTemplate redisTemplate;

    /**
     * 将商品信息保存到数据表 且 更新商品缓存
     * @param goodsInfos
     * @param taskId
     */
    @Async("getAsyncExecutor")//使用自定义线程池
    @Override
    public void asyncImportGoods(List<GoodsInfo> goodsInfos, String taskId) {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        log.info("async task running taskId: {}", taskId);
        StopWatch watch = StopWatch.createStarted();

        /**
         * 如果是goodsInfo中存在重复的商品，不保存；直接返回，记录错误日志
         * 请求数据是否合法的标记
         */
        boolean isIllegal = false;

        //将商品信息字段joint在一起，用来判断是否存在重复
        Set<String> goodsJointInfos = new HashSet<>(goodsInfos.size());
        //过滤出来的，可以入库的商品信息（根据业务需求，不一样）
        List<GoodsInfo> filteredGoodsInfo = new ArrayList<>(goodsInfos.size());

        // 走一遍循环, 过滤非法参数与判定当前请求是否合法
        for (GoodsInfo goods : goodsInfos) {

            // 基本条件不满足的, 直接过滤器
            if (goods.getPrice() <= 0 || goods.getSupply() <= 0) {
                log.info("goods info is invalid: [{}]", JSON.toJSONString(goods));
                continue;
            }

            // 组合商品信息
            String jointInfo = String.format(
                    "%s,%s,%s",
                    goods.getGoodsCategory(), goods.getBrandCategory(),
                    goods.getGoodsName()
            );
            if (goodsJointInfos.contains(jointInfo)) {
                isIllegal = true;
            }

            // 加入到两个容器中
            goodsJointInfos.add(jointInfo);
            filteredGoodsInfo.add(goods);
        }

        // 如果存在重复商品或者是没有需要入库的商品, 直接打印日志返回
        if (isIllegal || CollectionUtils.isEmpty(filteredGoodsInfo)) {
            watch.stop();
            log.warn("import nothing: {}", JSON.toJSONString(filteredGoodsInfo));
            log.info("check and import goods done: {}ms",
                    watch.getTime(TimeUnit.MILLISECONDS));
            return;
        }

        List<HemerocallisGoods> hemerocallisGoods = filteredGoodsInfo.stream()
                .map(HemerocallisGoods::to)
                .collect(Collectors.toList());
        List<HemerocallisGoods> targetGoods = new ArrayList<>(hemerocallisGoods.size());

        //保存goodsInfo之前先判断是否存在重复商品
        hemerocallisGoods.forEach(g -> {
            //limit 1
            if(null != hemerocallisGoodsRepository.findFirstByGoodsCategoryAndBrandCategoryAndGoodsName(
                    g.getGoodsCategory(), g.getBrandCategory(),
                    g.getGoodsName()
            ).orElse(null)) {
                return;
            }
            targetGoods.add(g);
        });

        //商品信息入库
        List<HemerocallisGoods> savedGoods = IterableUtils.toList(hemerocallisGoodsRepository.saveAll(targetGoods));
        // 将入库商品信息同步到 Redis 中
        saveNewGoodsInfoToRedis(savedGoods);

        log.info("save goods info to db and redis: {}", savedGoods.size());

        watch.stop();
        log.info("check and import goods success: {}ms",
                watch.getTime(TimeUnit.MILLISECONDS));

    }

    /**
     * 将保存到数据表中的数据缓存到 Redis 中
     * dict: key -> <id, SimpleGoodsInfo(json)>
     * */
    private void saveNewGoodsInfoToRedis(List<HemerocallisGoods> savedGoods) {

        // 由于 Redis 是内存存储, 只存储简单商品信息
        List<SimpleGoodsInfo> simpleGoodsInfos = savedGoods.stream()
                .map(HemerocallisGoods::toSimple)
                .collect(Collectors.toList());

        Map<String, String> id2JsonObject = new HashMap<>(simpleGoodsInfos.size());
        simpleGoodsInfos.forEach(
                g -> id2JsonObject.put(g.getId().toString(), JSON.toJSONString(g))
        );

        // 保存到 Redis 中
        redisTemplate.opsForHash().putAll(
                GoodsConstant.HEMEROCALLIS_GOODS_DICT_KEY,
                id2JsonObject
        );
    }

}
